#pragma comment(lib, "..\\common\\libs\\RenderEngineUtils.lib")
#pragma comment(lib, "..\\common\\libs\\Sgm.lib")
#pragma comment(lib, "ext_libs\\Image.lib")
#pragma comment(lib, "ext_libs\\ImagePNG.lib")
#pragma comment(lib, "ext_libs\\bass.lib")
#pragma comment(lib, "cg.lib")
#pragma comment(lib, "cggl.lib")

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")

#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>

#include "resource.h"
#include "..\DemoController.h"

extern void MouseLook(WORD cx_screen, WORD cy_screen, WORD x, WORD y);
extern BOOL CALLBACK dpStart(HWND hdlg, UINT message, WPARAM wparam, LPARAM lparam);

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

DWORD scr_cx_res;
DWORD scr_cy_res;
DWORD scr_bpp;
DWORD scr_freq;

HGLRC		hRC = NULL;
HDC			hDC = NULL;
HWND		hWnd = NULL;
HINSTANCE	hInstance;

BOOL		active = TRUE;
BOOL		keys[256];

GLvoid ReSizeGLScene(int width, int height)
{
	glViewport(0, 0, width, height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(60.0f, (float)width / (float)height, 0.01f, 100.0f);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void KillGLWindow()
{
	ChangeDisplaySettings(NULL, 0);
	ShowCursor(TRUE);

	if (hRC)
	{
		wglMakeCurrent(NULL, NULL);
		wglDeleteContext(hRC);
		hRC = NULL;
	}

	if (hDC)
	{
		ReleaseDC(hWnd, hDC);
		hDC = NULL;
	}

	if (hWnd)
	{
		DestroyWindow(hWnd);
		hWnd = NULL;
	}

	UnregisterClass("OpenGL", hInstance);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{	
	case WM_SYSCOMMAND:
		switch (wParam)
		{
		case SC_SCREENSAVE:
		case SC_MONITORPOWER:
			return 0;
		}
		break;

	case WM_CLOSE:
		PostQuitMessage(0);
		return 0;

	case WM_KEYDOWN:
		keys[wParam] = TRUE;
		return 0;

	case WM_KEYUP:
		keys[wParam] = FALSE;
		return 0;
	}

	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

bool CreateGLWindow(char * title, int width, int height, int bits, int scr_freq)
{
	GLuint		PixelFormat;
	WNDCLASS	wc;
	DWORD		dwExStyle;
	DWORD		dwStyle;

	RECT		WindowRect;
	WindowRect.left		= (long) 0;
	WindowRect.right	= (long) width;
	WindowRect.top		= (long) 0;
	WindowRect.bottom	= (long) height;

	hInstance	= GetModuleHandle(NULL);

	wc.style	= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc	= (WNDPROC) WndProc;
	wc.cbClsExtra	= 0;
	wc.cbWndExtra	= 0;
	wc.hInstance	= hInstance;
	wc.hIcon		= LoadIcon(NULL, IDI_WINLOGO);
	wc.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground= NULL;
	wc.lpszMenuName = NULL;
	wc.lpszClassName= "OpenGL";


	if (!RegisterClass(&wc))
	{
		MessageBox(NULL, "Falied To Register Window Class",	"ERROR", MB_OK);
		return FALSE;
	}

	DEVMODE dmScreenSettings;
	memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
	dmScreenSettings.dmSize			= sizeof(dmScreenSettings);
	dmScreenSettings.dmPelsWidth	= width;
	dmScreenSettings.dmPelsHeight	= height;
	dmScreenSettings.dmBitsPerPel	= bits;
	dmScreenSettings.dmDisplayFrequency = scr_freq;
	dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT |DM_DISPLAYFREQUENCY;

	if (ChangeDisplaySettings(&dmScreenSettings,
		CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
	{
		MessageBox(NULL, "Unable To Run In Fullscreen!", "ERROR", MB_OK);
		MessageBox(NULL, "Program Will Now Quit", "ERROR", MB_OK);
		return FALSE;
	}

	dwExStyle	= WS_EX_APPWINDOW;
	dwStyle		= WS_POPUP;
	ShowCursor(FALSE);

	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);

	if (!(hWnd = CreateWindowEx(dwExStyle,
								"OpenGL",
								title,
								WS_CLIPSIBLINGS |
								WS_CLIPCHILDREN |
								dwStyle,
								0, 0,
								WindowRect.right - WindowRect.left,
								WindowRect.bottom - WindowRect.top,
								NULL,
								NULL,
								hInstance,
								NULL)))
	{
		KillGLWindow();
		MessageBox(NULL, "Window Creation Error.", "ERROR", MB_OK);
		return FALSE;
	}

	static PIXELFORMATDESCRIPTOR pfd = 
	{
		sizeof (PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW |
		PFD_SUPPORT_OPENGL |
		PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		bits,
		0, 0, 0, 0, 0, 0,
		0,
		0,
		0,
		0, 0, 0, 0,
		32,
		0,
		0,
		PFD_MAIN_PLANE,
		0,
		0, 0, 0
	};

	if (!(hDC = GetDC(hWnd)))
	{
		KillGLWindow();
		MessageBox(NULL, "Can`t Create A GL Device Context",
			"ERROR", MB_OK);
		return FALSE;
	}

	if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd)))
	{
		KillGLWindow();
		MessageBox(NULL, "Can`t Find A Suitable PixelFormat",
			"ERROR", MB_OK);
		return FALSE;
	}

	if (!SetPixelFormat(hDC, PixelFormat, &pfd))
	{
		KillGLWindow();
		MessageBox(NULL, "Can`t Set The PixelFormat",
			"ERROR", MB_OK);
		return FALSE;
	}

	if (!(hRC = wglCreateContext(hDC)))
	{
		KillGLWindow();
		MessageBox(NULL, "Can`t Create A GL Rendering Context",
			"ERROR", MB_OK);
		return FALSE;
	}

	if (!wglMakeCurrent(hDC, hRC))
	{
		KillGLWindow();
		MessageBox(NULL, "Can`t Activate The GL Rendering Context",
			"ERROR", MB_OK);
		return FALSE;
	}

	ShowWindow(hWnd, SW_SHOW);
	SetForegroundWindow(hWnd);
	SetFocus(hWnd);
	ReSizeGLScene(width, height);

	return true;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
{
	MSG		msg;
	BOOL	done = false;

	if (DialogBox(hInstance, MAKEINTRESOURCE(IDD_START), hWnd, dpStart))
	{
		if (!CreateGLWindow("asmCode", scr_cx_res, scr_cy_res, scr_bpp, scr_freq))
		{
			return 0;
		}

		IDemoController *demo = new DemoController(hDC);
		demo ->Initialize();
		demo ->Start();

		while (!done)
		{
			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
			{
				if (msg.message == WM_QUIT)
				{
					done = TRUE;
				}
				else
				{
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
			}
			else
			{
				if (keys[VK_ESCAPE])
				{
					done = true;
				}
				else
				{
					demo ->Process();
				}
			}
		}

		demo ->Stop();

		if (demo != NULL)
			delete demo;

		KillGLWindow();

		return (int)(msg.wParam);
	}

	return 0;
}
